{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tour rápido"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Modelos de difusão são treinados para remover o ruído Gaussiano aleatório passo a passo para gerar uma amostra de interesse, como uma imagem ou áudio. Isso despertou um tremendo interesse em IA generativa, e você provavelmente já viu exemplos de imagens geradas por difusão na internet. 🧨 Diffusers é uma biblioteca que visa tornar os modelos de difusão amplamente acessíveis a todos.\n",
    "\n",
    "Seja você um desenvolvedor ou um usuário, esse tour rápido irá introduzir você ao 🧨 Diffusers e ajudar você a começar a gerar rapidamente! Há três componentes principais da biblioteca para conhecer:\n",
    "\n",
    "- O `DiffusionPipeline` é uma classe de alto nível de ponta a ponta desenhada para gerar rapidamente amostras de modelos de difusão pré-treinados para inferência.\n",
    "- [Modelos](https://huggingface.co/docs/diffusers/main/pt/./api/models) pré-treinados populares e módulos que podem ser usados como blocos de construção para criar sistemas de difusão.\n",
    "- Vários [Agendadores](https://huggingface.co/docs/diffusers/main/pt/./api/schedulers/overview) diferentes - algoritmos que controlam como o ruído é adicionado para treinamento, e como gerar imagens sem o ruído durante a inferência.\n",
    "\n",
    "Esse tour rápido mostrará como usar o `DiffusionPipeline` para inferência, e então mostrará como combinar um modelo e um agendador para replicar o que está acontecendo dentro do `DiffusionPipeline`.\n",
    "\n",
    "> [!TIP]\n",
    "> Esse tour rápido é uma versão simplificada da introdução 🧨 Diffusers [notebook](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/diffusers_intro.ipynb) para ajudar você a começar rápido. Se você quer aprender mais sobre o objetivo do 🧨 Diffusers, filosofia de design, e detalhes adicionais sobre a API principal, veja o notebook!\n",
    "\n",
    "Antes de começar, certifique-se de ter todas as bibliotecas necessárias instaladas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# uncomment to install the necessary libraries in Colab\n",
    "#!pip install --upgrade diffusers accelerate transformers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [🤗 Accelerate](https://huggingface.co/docs/accelerate/index) acelera o carregamento do modelo para geração e treinamento.\n",
    "- [🤗 Transformers](https://huggingface.co/docs/transformers/index) é necessário para executar os modelos mais populares de difusão, como o [Stable Diffusion](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/overview)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DiffusionPipeline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "O `DiffusionPipeline` é a forma mais fácil de usar um sistema de difusão pré-treinado para geração. É um sistema de ponta a ponta contendo o modelo e o agendador. Você pode usar o `DiffusionPipeline` pronto para muitas tarefas. Dê uma olhada na tabela abaixo para algumas tarefas suportadas, e para uma lista completa de tarefas suportadas, veja a tabela [Resumo do 🧨 Diffusers](https://huggingface.co/docs/diffusers/main/pt/./api/pipelines/overview#diffusers-summary).\n",
    "\n",
    "| **Tarefa**                             | **Descrição**                                                                                                             | **Pipeline**                                                                       |\n",
    "| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |\n",
    "| Unconditional Image Generation         | gera uma imagem a partir do ruído Gaussiano                                                                               | [unconditional_image_generation](https://huggingface.co/docs/diffusers/main/pt/./using-diffusers/unconditional_image_generation) |\n",
    "| Text-Guided Image Generation           | gera uma imagem a partir de um prompt de texto                                                                            | [conditional_image_generation](https://huggingface.co/docs/diffusers/main/pt/./using-diffusers/conditional_image_generation)     |\n",
    "| Text-Guided Image-to-Image Translation | adapta uma imagem guiada por um prompt de texto                                                                           | [img2img](https://huggingface.co/docs/diffusers/main/pt/./using-diffusers/img2img)                                               |\n",
    "| Text-Guided Image-Inpainting           | preenche a parte da máscara da imagem, dado a imagem, a máscara e o prompt de texto                                       | [inpaint](https://huggingface.co/docs/diffusers/main/pt/./using-diffusers/inpaint)                                               |\n",
    "| Text-Guided Depth-to-Image Translation | adapta as partes de uma imagem guiada por um prompt de texto enquanto preserva a estrutura por estimativa de profundidade | [depth2img](https://huggingface.co/docs/diffusers/main/pt/./using-diffusers/depth2img)                                           |\n",
    "\n",
    "Comece criando uma instância do `DiffusionPipeline` e especifique qual checkpoint do pipeline você gostaria de baixar.\n",
    "Você pode usar o `DiffusionPipeline` para qualquer [checkpoint](https://huggingface.co/models?library=diffusers&sort=downloads) armazenado no Hugging Face Hub.\n",
    "Nesse quicktour, você carregará o checkpoint [`stable-diffusion-v1-5`](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5) para geração de texto para imagem.\n",
    "\n",
    "> [!WARNING]\n",
    "> Para os modelos de [Stable Diffusion](https://huggingface.co/CompVis/stable-diffusion), por favor leia cuidadosamente a [licença](https://huggingface.co/spaces/CompVis/stable-diffusion-license) primeiro antes de rodar o modelo. 🧨 Diffusers implementa uma verificação de segurança: [`safety_checker`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/safety_checker.py) para prevenir conteúdo ofensivo ou nocivo, mas as capacidades de geração de imagem aprimorada do modelo podem ainda produzir conteúdo potencialmente nocivo.\n",
    "\n",
    "Para carregar o modelo com o método `from_pretrained()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from diffusers import DiffusionPipeline\n",
    "\n",
    "pipeline = DiffusionPipeline.from_pretrained(\"stable-diffusion-v1-5/stable-diffusion-v1-5\", use_safetensors=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "O `DiffusionPipeline` baixa e armazena em cache todos os componentes de modelagem, tokenização, e agendamento. Você verá que o pipeline do Stable Diffusion é composto pelo `UNet2DConditionModel` e `PNDMScheduler` entre outras coisas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "StableDiffusionPipeline {\n",
       "  \"_class_name\": \"StableDiffusionPipeline\",\n",
       "  \"_diffusers_version\": \"0.13.1\",\n",
       "  ...,\n",
       "  \"scheduler\": [\n",
       "    \"diffusers\",\n",
       "    \"PNDMScheduler\"\n",
       "  ],\n",
       "  ...,\n",
       "  \"unet\": [\n",
       "    \"diffusers\",\n",
       "    \"UNet2DConditionModel\"\n",
       "  ],\n",
       "  \"vae\": [\n",
       "    \"diffusers\",\n",
       "    \"AutoencoderKL\"\n",
       "  ]\n",
       "}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pipeline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nós fortemente recomendamos rodar o pipeline em uma placa de vídeo, pois o modelo consiste em aproximadamente 1.4 bilhões de parâmetros.\n",
    "Você pode mover o objeto gerador para uma placa de vídeo, assim como você faria no PyTorch:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipeline.to(\"cuda\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Agora você pode passar o prompt de texto para o `pipeline` para gerar uma imagem, e então acessar a imagem sem ruído. Por padrão, a saída da imagem é embrulhada em um objeto [`PIL.Image`](https://pillow.readthedocs.io/en/stable/reference/Image.html?highlight=image#the-image-class)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "image = pipeline(\"An image of a squirrel in Picasso style\").images[0]\n",
    "image"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"flex justify-center\">\n",
    "    <img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/image_of_squirrel_painting.png\"/>\n",
    "</div>\n",
    "\n",
    "Salve a imagem chamando o `save`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "image.save(\"image_of_squirrel_painting.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pipeline local"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Você também pode utilizar o pipeline localmente. A única diferença é que você precisa baixar os pesos primeiro:\n",
    "\n",
    "```bash\n",
    "!git lfs install\n",
    "!git clone https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5\n",
    "```\n",
    "\n",
    "Assim carregue os pesos salvos no pipeline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipeline = DiffusionPipeline.from_pretrained(\"./stable-diffusion-v1-5\", use_safetensors=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Agora você pode rodar o pipeline como você faria na seção acima."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Troca dos agendadores"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Agendadores diferentes tem diferentes velocidades de retirar o ruído e compensações de qualidade. A melhor forma de descobrir qual funciona melhor para você é testar eles! Uma das principais características do 🧨 Diffusers é permitir que você troque facilmente entre agendadores. Por exemplo, para substituir o `PNDMScheduler` padrão com o `EulerDiscreteScheduler`, carregue ele com o método `from_config()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from diffusers import EulerDiscreteScheduler\n",
    "\n",
    "pipeline = DiffusionPipeline.from_pretrained(\"stable-diffusion-v1-5/stable-diffusion-v1-5\", use_safetensors=True)\n",
    "pipeline.scheduler = EulerDiscreteScheduler.from_config(pipeline.scheduler.config)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tente gerar uma imagem com o novo agendador e veja se você nota alguma diferença!\n",
    "\n",
    "Na próxima seção, você irá dar uma olhada mais de perto nos componentes - o modelo e o agendador - que compõe o `DiffusionPipeline` e aprender como usar esses componentes para gerar uma imagem de um gato."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Modelos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A maioria dos modelos recebe uma amostra de ruído, e em cada _timestep_ ele prevê o _noise residual_ (outros modelos aprendem a prever a amostra anterior diretamente ou a velocidade ou [`v-prediction`](https://github.com/huggingface/diffusers/blob/5e5ce13e2f89ac45a0066cb3f369462a3cf1d9ef/src/diffusers/schedulers/scheduling_ddim.py#L110)), a diferença entre uma imagem menos com ruído e a imagem de entrada. Você pode misturar e combinar modelos para criar outros sistemas de difusão.\n",
    "\n",
    "Modelos são inicializados com o método `from_pretrained()` que também armazena em cache localmente os pesos do modelo para que seja mais rápido na próxima vez que você carregar o modelo. Para o tour rápido, você irá carregar o `UNet2DModel`, um modelo básico de geração de imagem incondicional com um checkpoint treinado em imagens de gato:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from diffusers import UNet2DModel\n",
    "\n",
    "repo_id = \"google/ddpm-cat-256\"\n",
    "model = UNet2DModel.from_pretrained(repo_id, use_safetensors=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para acessar os parâmetros do modelo, chame `model.config`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.config"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A configuração do modelo é um dicionário 🧊 congelado 🧊, o que significa que esses parâmetros não podem ser mudados depois que o modelo é criado. Isso é intencional e garante que os parâmetros usados para definir a arquitetura do modelo no início permaneçam os mesmos, enquanto outros parâmetros ainda podem ser ajustados durante a geração.\n",
    "\n",
    "Um dos parâmetros mais importantes são:\n",
    "\n",
    "- `sample_size`: a dimensão da altura e largura da amostra de entrada.\n",
    "- `in_channels`: o número de canais de entrada da amostra de entrada.\n",
    "- `down_block_types` e `up_block_types`: o tipo de blocos de downsampling e upsampling usados para criar a arquitetura UNet.\n",
    "- `block_out_channels`: o número de canais de saída dos blocos de downsampling; também utilizado como uma order reversa do número de canais de entrada dos blocos de upsampling.\n",
    "- `layers_per_block`: o número de blocks ResNet presentes em cada block UNet.\n",
    "\n",
    "Para usar o modelo para geração, crie a forma da imagem com ruído Gaussiano aleatório. Deve ter um eixo `batch` porque o modelo pode receber múltiplos ruídos aleatórios, um eixo `channel` correspondente ao número de canais de entrada, e um eixo `sample_size` para a altura e largura da imagem:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 3, 256, 256])"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "torch.manual_seed(0)\n",
    "\n",
    "noisy_sample = torch.randn(1, model.config.in_channels, model.config.sample_size, model.config.sample_size)\n",
    "noisy_sample.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para geração, passe a imagem com ruído para o modelo e um `timestep`. O `timestep` indica o quão ruidosa a imagem de entrada é, com mais ruído no início e menos no final. Isso ajuda o modelo a determinar sua posição no processo de difusão, se está mais perto do início ou do final. Use o método `sample` para obter a saída do modelo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with torch.no_grad():\n",
    "    noisy_residual = model(sample=noisy_sample, timestep=2).sample"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para geração de exemplos reais, você precisará de um agendador para guiar o processo de retirada do ruído. Na próxima seção, você irá aprender como acoplar um modelo com um agendador."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Agendadores"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Agendadores gerenciam a retirada do ruído de uma amostra ruidosa para uma amostra menos ruidosa dado a saída do modelo - nesse caso, é o `noisy_residual`.\n",
    "\n",
    "> [!TIP]\n",
    "> 🧨 Diffusers é uma caixa de ferramentas para construir sistemas de difusão. Enquanto o `DiffusionPipeline` é uma forma conveniente de começar com um sistema de difusão pré-construído, você também pode escolher seus próprios modelos e agendadores separadamente para construir um sistema de difusão personalizado.\n",
    "\n",
    "Para o tour rápido, você irá instanciar o `DDPMScheduler` com o método `from_config()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DDPMScheduler {\n",
       "  \"_class_name\": \"DDPMScheduler\",\n",
       "  \"_diffusers_version\": \"0.13.1\",\n",
       "  \"beta_end\": 0.02,\n",
       "  \"beta_schedule\": \"linear\",\n",
       "  \"beta_start\": 0.0001,\n",
       "  \"clip_sample\": true,\n",
       "  \"clip_sample_range\": 1.0,\n",
       "  \"num_train_timesteps\": 1000,\n",
       "  \"prediction_type\": \"epsilon\",\n",
       "  \"trained_betas\": null,\n",
       "  \"variance_type\": \"fixed_small\"\n",
       "}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from diffusers import DDPMScheduler\n",
    "\n",
    "scheduler = DDPMScheduler.from_config(repo_id)\n",
    "scheduler"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> [!TIP]\n",
    "> 💡 Perceba como o agendador é instanciado de uma configuração. Diferentemente de um modelo, um agendador não tem pesos treináveis e é livre de parâmetros!\n",
    "\n",
    "Um dos parâmetros mais importante são:\n",
    "\n",
    "- `num_train_timesteps`: o tamanho do processo de retirar ruído ou em outras palavras, o número de _timesteps_ necessários para o processo de ruídos Gausianos aleatórios dentro de uma amostra de dados.\n",
    "- `beta_schedule`: o tipo de agendados de ruído para o uso de geração e treinamento.\n",
    "- `beta_start` e `beta_end`: para começar e terminar os valores de ruído para o agendador de ruído.\n",
    "\n",
    "Para predizer uma imagem com um pouco menos de ruído, passe o seguinte para o método do agendador `step()`: saída do modelo, `timestep`, e a atual `amostra`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "less_noisy_sample = scheduler.step(model_output=noisy_residual, timestep=2, sample=noisy_sample).prev_sample\n",
    "less_noisy_sample.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "O `less_noisy_sample` pode ser passado para o próximo `timestep` onde ele ficará ainda com menos ruído! Vamos juntar tudo agora e visualizar o processo inteiro de retirada de ruído.\n",
    "\n",
    "Comece, criando a função que faça o pós-processamento e mostre a imagem sem ruído como uma `PIL.Image`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import PIL.Image\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "def display_sample(sample, i):\n",
    "    image_processed = sample.cpu().permute(0, 2, 3, 1)\n",
    "    image_processed = (image_processed + 1.0) * 127.5\n",
    "    image_processed = image_processed.numpy().astype(np.uint8)\n",
    "\n",
    "    image_pil = PIL.Image.fromarray(image_processed[0])\n",
    "    display(f\"Image at step {i}\")\n",
    "    display(image_pil)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para acelerar o processo de retirada de ruído, mova a entrada e o modelo para uma GPU:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.to(\"cuda\")\n",
    "noisy_sample = noisy_sample.to(\"cuda\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Agora, crie um loop de retirada de ruído que prediz o residual da amostra menos ruidosa, e computa a amostra menos ruidosa com o agendador:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tqdm\n",
    "\n",
    "sample = noisy_sample\n",
    "\n",
    "for i, t in enumerate(tqdm.tqdm(scheduler.timesteps)):\n",
    "    # 1. predict noise residual\n",
    "    with torch.no_grad():\n",
    "        residual = model(sample, t).sample\n",
    "\n",
    "    # 2. compute less noisy image and set x_t -> x_t-1\n",
    "    sample = scheduler.step(residual, t, sample).prev_sample\n",
    "\n",
    "    # 3. optionally look at image\n",
    "    if (i + 1) % 50 == 0:\n",
    "        display_sample(sample, i + 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sente-se e assista o gato ser gerado do nada além de ruído! 😻\n",
    "\n",
    "<div class=\"flex justify-center\">\n",
    "    <img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/diffusion-quicktour.png\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Próximos passos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esperamos que você tenha gerado algumas imagens legais com o 🧨 Diffusers neste tour rápido! Para suas próximas etapas, você pode\n",
    "\n",
    "- Treine ou faça a configuração fina de um modelo para gerar suas próprias imagens no tutorial de [treinamento](https://huggingface.co/docs/diffusers/main/pt/./tutorials/basic_training).\n",
    "- Veja exemplos oficiais e da comunidade de [scripts de treinamento ou configuração fina](https://github.com/huggingface/diffusers/tree/main/examples#-diffusers-examples) para os mais variados casos de uso.\n",
    "- Aprenda sobre como carregar, acessar, mudar e comparar agendadores no guia [Usando diferentes agendadores](https://huggingface.co/docs/diffusers/main/pt/./using-diffusers/schedulers).\n",
    "- Explore engenharia de prompt, otimizações de velocidade e memória, e dicas e truques para gerar imagens de maior qualidade com o guia [Stable Diffusion](https://huggingface.co/docs/diffusers/main/pt/./stable_diffusion).\n",
    "- Se aprofunde em acelerar 🧨 Diffusers com guias sobre [PyTorch otimizado em uma GPU](https://huggingface.co/docs/diffusers/main/pt/./optimization/fp16), e guias de inferência para rodar [Stable Diffusion em Apple Silicon (M1/M2)](https://huggingface.co/docs/diffusers/main/pt/./optimization/mps) e [ONNX Runtime](https://huggingface.co/docs/diffusers/main/pt/./optimization/onnx)."
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 4
}
